package ptij.ADSL.pn.ATUR;

import ptolemy.kernel.*;
import ptolemy.kernel.util.*;
import ptolemy.data.*;
import ptolemy.data.expr.*;
import ptolemy.actor.*;
import ptolemy.math.Complex;

public class DFT extends TypedAtomicActor {

    public TypedIOPort input;
    public TypedIOPort subChannel[];

    public DFT(TypedCompositeActor container, String name) throws
        NameDuplicationException, IllegalActionException {
        super(container, name);
		
	input = new TypedIOPort(this, "input", true, false);
	input.setTypeEquals(DoubleToken.class);

	subChannel = new TypedIOPort[255];
	for(int i = 0;i < 255;i++) {
	    subChannel[i] = new TypedIOPort(this, "subChannel_" + i,
	        false, true);
	    subChannel[i].setTypeEquals(DoubleToken.class);
	}
    }

    public void fire() throws IllegalActionException {        	

	for(int i = 0;i < 1024;i += 2) {
	    DoubleToken dt = (DoubleToken) input.get(0);
	    _data[i] = dt.doubleValue();
	    _data[i+1] = 0;
	}
	
	_dft();

        for(int i = 1;i < 256;i++) {
	    subChannel[i-1].broadcast(new DoubleToken(Math.round(_data[2*i])));
	    subChannel[i-1].broadcast(new DoubleToken(Math.round(_data[2*i + 1])));
	}	
    }
    
    public void initialize() throws IllegalActionException {
        super.initialize();	
	_data = new double[1024];
    }
    
    private void _dft() {
        transform_internal(-1);
    }

    private void transform_internal(int direction) {

	int n = 512;
	int logn = 9;

        bitreverse();

        for(int bit = 0, dual = 1; bit < logn; bit++, dual *= 2) {
            double w_real = 1.0;
            double w_imag = 0.0;

            double theta = 2.0 * direction * Math.PI / (2.0 * (double) dual);
            double s = Math.sin(theta);
            double t = Math.sin(theta / 2.0);
            double s2 = 2.0 * t * t;
       
            for (int b = 0; b < n; b += 2 * dual) {
                int i = 2*b;
                int j = 2*(b + dual);

                double wd_real = _data[j] ;
                double wd_imag = _data[j+1] ;
          
                _data[j] = _data[i] - wd_real;
                _data[j+1] = _data[i+1] - wd_imag;
                _data[i] += wd_real;
                _data[i+1] += wd_imag;
            }
      
            for (int a = 1; a < dual; a++) {
                { 
                    double tmp_real = w_real - s * w_imag - s2 * w_real;
                    double tmp_imag = w_imag + s * w_real - s2 * w_imag;
                    w_real = tmp_real;
                    w_imag = tmp_imag;
                }
                
		for(int b = 0; b < n; b += 2 * dual) {
                    int i = 2*(b + a);
                    int j = 2*(b + a + dual);

                    double z1_real = _data[j];
                    double z1_imag = _data[j+1];
              
                    double wd_real = w_real * z1_real - w_imag * z1_imag;
                    double wd_imag = w_real * z1_imag + w_imag * z1_real;

                    _data[j] = _data[i] - wd_real;
                    _data[j+1] = _data[i+1] - wd_imag;
                    _data[i]  += wd_real;
                    _data[i+1]+= wd_imag;
                }
            }
        }
    }

    private void bitreverse() {
	int i = 0; 
	int j = 0;
    
        for(; i < 511; i++) {

            int ii = i << 1;
            int jj = j << 1;
            int k = 512 >> 1;

            if (i < j) {
                double tmp_real = _data[ii];
                double tmp_imag = _data[ii+1];
                _data[ii] = _data[jj];
                _data[ii+1] = _data[jj+1];
                _data[jj] = tmp_real;
                _data[jj+1] = tmp_imag;
	    }
            while(k <= j) {
		j -= k;
                k >>= 1 ; 
	    }
            j += k;
        }
    }
        
    private double _data[];
}
